﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace ChatBackend.Common.Snowflake
{
    public class SnowflakeIdGenerator
    {
        // 常量定义
        private const long Twepoch = 1288834974657L; // 纪元时间，这个时间是一个常数，通常取系统上线时间

        private const int WorkerIdBits = 5; // 机器标识位数
        private const int DatacenterIdBits = 5; // 数据中心标识位数
        private const int SequenceBits = 12; // 序列号位数

        // 最大值计算
        private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); // 支持的最大机器id，结果是31

        private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); // 支持的最大数据中心id，结果是31
        private const long SequenceMask = -1L ^ (-1L << SequenceBits); // 生成序列的掩码，这里为4095

        // 移位操作
        private const int WorkerIdShift = SequenceBits; // 机器ID的偏移量（12）

        private const int DatacenterIdShift = SequenceBits + WorkerIdBits; // 数据中心ID的偏移量（17）
        private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; // 时间戳的偏移量（22）

        // 变量定义
        private long _lastTimestamp = -1L; // 存储上次生成ID的时间戳

        private long _sequence = 0L; // 序列号

        // 属性定义
        public long WorkerId { get; protected set; } = 0; // 工作机器ID

        public long DatacenterId { get; protected set; } = 0; // 数据中心ID

        // 构造函数
        public SnowflakeIdGenerator(long workerId, long datacenterId)
        {
            // 检查工作机器ID和数据中心ID是否超出最大值
            if (workerId > MaxWorkerId || workerId < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(workerId), $"Worker ID can't be greater than {MaxWorkerId} or less than 0");
            }

            if (datacenterId > MaxDatacenterId || datacenterId < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(datacenterId), $"Datacenter ID can't be greater than {MaxDatacenterId} or less than 0");
            }

            // 设置工作机器ID和数据中心ID
            WorkerId = workerId;
            DatacenterId = datacenterId;
        }

        // 生成ID的方法
        public long NextId()
        {
            lock (this) // 确保线程安全
            {
                var timestamp = TimeGen(); // 获取当前时间戳

                // 如果当前时间戳小于上次ID生成的时间戳，说明系统时钟回拨了，这是不允许的
                if (timestamp < _lastTimestamp)
                {
                    throw new InvalidOperationException($"Clock moved backwards. Refusing to generate id for {_lastTimestamp - timestamp} milliseconds");
                }

                // 如果是同一时间生成的，则进行毫秒内序列
                if (_lastTimestamp == timestamp)
                {
                    _sequence = (_sequence + 1) & SequenceMask; // 序列号递增，并与序列掩码进行与操作，确保序列号在指定范围内循环
                    if (_sequence == 0) // 序列号溢出，即超过了4095
                    {
                        timestamp = TilNextMillis(_lastTimestamp); // 阻塞到下一个毫秒，获得新的时间戳
                    }
                }
                else
                {
                    // 时间戳改变，毫秒内序列重置
                    _sequence = 0L;
                }

                // 更新上次生成ID的时间戳
                _lastTimestamp = timestamp;

                // 移位并通过或运算拼到一起组成64位的ID
                return ((timestamp - Twepoch) << TimestampLeftShift) | // 时间戳部分
                       (DatacenterId << DatacenterIdShift) | // 数据中心ID部分
                       (WorkerId << WorkerIdShift) | // 工作机器ID部分
                       _sequence; // 序列号部分
            }
        }

        // 等待到下一个毫秒的方法
        private long TilNextMillis(long lastTimestamp)
        {
            var timestamp = TimeGen();
            while (timestamp <= lastTimestamp)
            {
                timestamp = TimeGen();
            }
            return timestamp;
        }

        // 获取当前时间戳的方法
        private long TimeGen()
        {
            return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        }
    }

    public class SnowflakeIdStatic
    {
        private static readonly SnowflakeIdGenerator _snowflakeIdGenerator;

        static SnowflakeIdStatic()
        {
            _snowflakeIdGenerator = new SnowflakeIdGenerator(0, 0);
        }

        public static BigInteger GetId()
        {
            return _snowflakeIdGenerator.NextId();
        }
    }
}